#include <cmath>		// abs, ceil, floor, isnan, log, log10, log2, pow, round, sqrt
#include <iomanip>
#include <iostream>
#include <mutex>
#include <sstream>
#include <type_traits>	// is_floating_point_v, is_signed_v, is_unsigned_v

#include "mini-log.hpp"

using oss_t = std::ostringstream;

namespace mini_log {

std::mutex g_cout_mutex;
std::mutex g_cerr_mutex;
thread_local str_t tl_thread_name;

void reg_thread( const str_t& n_ ) {
	tl_thread_name = n_;
};

void log_stdout( const str_t& log_ ) {
	std::lock_guard<std::mutex> only_1_thread_can_pass_at_a_time( g_cout_mutex );

	std::cout << tl_thread_name << ':' << log_ << std::endl;
};

void log_stderr( const str_t& log_ ) {
	std::lock_guard<std::mutex> only_1_thread_can_pass_at_a_time( g_cerr_mutex );

	std::cerr << tl_thread_name << ':' << log_ << std::endl;
};

struct CustomGrouper_t : std::numpunct<char> {
	CustomGrouper_t( char grp_w_, char grp_c_  = '\'', size_t persist_ = 1 ):
		std::numpunct<char>( persist_ ), group_w( grp_w_ ), group_c( grp_c_ ) {};

	/* GrouingDigits_t( char grp_c_, size_t persist_ = 1 )
		: std::numpunct<char>( persist_ ), group_c( grp_c_ ) {};*/

	char do_thousands_sep() const override {
		return group_c;
	};
	str_t do_grouping() const override {
		return str_t( 1, group_w );
	};

	char group_w = 0;		// 隔几位分一下？
	char group_c = '\'';	// 缺省的分隔符
};

thread_local CustomGrouper_t tl_grouper( '\0', '\'', 1 );

template<typename T>
str_t fmt( const T v_, size_t w_, size_t p_, size_t g_, char f_, char s_ ) {

	// 浮点数的特殊值
	if( std::is_floating_point_v<T> ) {
		if( std::isnan( v_ ) )
			return ( str_t( w_, f_ ) + "nan" ).substr( 3, w_ );
		if( std::isinf( v_ ) )
			return ( str_t( w_, f_ ) + "inf" ).substr( 3, w_ );
	}

	oss_t oss;
	tl_grouper.group_w = static_cast<char>( g_ );
	tl_grouper.group_c = s_;
	std::locale locGrpDigits( oss.getloc(), &tl_grouper );
	oss.imbue( locGrpDigits );

	oss << std::fixed
		<< std::setw( w_ )
		<< std::setprecision( p_ )
		<< std::setfill( f_ );

	// 单字节的整数总是会被当作 char 输出,只好先转为整数再输出
	if( sizeof( T ) > 1 )
		oss << v_;
	else
		oss << static_cast<int>( v_ );

	str_t result( oss.str() );
	if( w_ > 0 && result.length() > w_ )
		return str_t( w_, '*' );
	else
		return result;
};

// 显式地实例化一下,以免链接时找不到
template str_t fmt<int8_t>( const int8_t,		size_t, size_t, size_t, char, char );
template str_t fmt<uint8_t>( const uint8_t,		size_t, size_t, size_t, char, char );
template str_t fmt<int16_t>( const int16_t,		size_t, size_t, size_t, char, char );
template str_t fmt<uint16_t>( const uint16_t,	size_t, size_t, size_t, char, char );
template str_t fmt<int32_t>( const int32_t,		size_t, size_t, size_t, char, char );
template str_t fmt<uint32_t>( const uint32_t,	size_t, size_t, size_t, char, char );
template str_t fmt<int64_t>( const int64_t,		size_t, size_t, size_t, char, char );
template str_t fmt<uint64_t>( const uint64_t,	size_t, size_t, size_t, char, char );
template str_t fmt<float>( const float,			size_t, size_t, size_t, char, char );
template str_t fmt<double>( const double,		size_t, size_t, size_t, char, char );

};	// namespace mini_log

// kate: indent-mode cstyle; indent-width 4; replace-tabs off; tab-width 4;
